﻿// Decompiled with JetBrains decompiler
// Type: Netick.ExecutionOrderSystem
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Reflection;

#nullable disable
namespace Netick;

internal static class ExecutionOrderSystem
{
  public static Dictionary<Type, int> ProcessAndGetOrders(List<Type> types)
  {
    List<ExecutionOrderSystem.ScriptExecutionOrderDefinition> orderDefinitions = ExecutionOrderSystem.GetExecutionOrderDefinitions(types);
    List<ExecutionOrderSystem.ScriptExecutionOrderDependency> orderDependencies = ExecutionOrderSystem.GetExecutionOrderDependencies(types);
    Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>> graph = ExecutionOrderSystem.Graph.Create(orderDefinitions, orderDependencies);
    if (ExecutionOrderSystem.Graph.IsCyclic(graph))
    {
      NetickLogger.LogError((object) "Circular script execution order definitions");
      return (Dictionary<Type, int>) null;
    }
    List<Type> roots = ExecutionOrderSystem.Graph.GetRoots(graph);
    Dictionary<Type, int> initialExecutionOrder = ExecutionOrderSystem.GetInitialExecutionOrder(orderDefinitions, roots, true);
    ExecutionOrderSystem.Graph.PropagateValues(graph, initialExecutionOrder);
    return initialExecutionOrder;
  }

  private static List<ExecutionOrderSystem.ScriptExecutionOrderDependency> GetExecutionOrderDependencies(
    List<Type> types)
  {
    List<ExecutionOrderSystem.ScriptExecutionOrderDependency> orderDependencies = new List<ExecutionOrderSystem.ScriptExecutionOrderDependency>();
    foreach (Type type in types)
    {
      bool flag1 = Attribute.IsDefined((MemberInfo) type, typeof (ExecutionOrderAttribute));
      bool flag2 = Attribute.IsDefined((MemberInfo) type, typeof (ExecuteAfterAttribute));
      bool flag3 = Attribute.IsDefined((MemberInfo) type, typeof (ExecuteBeforeAttribute));
      if (flag2)
      {
        if (flag1)
        {
          NetickLogger.LogError((object) $"Script {type.Name} has both [ExecutionOrder] and [ExecuteAfter] attributes. Ignoring the [ExecuteAfter] attribute.");
          continue;
        }
        foreach (ExecuteAfterAttribute customAttribute in (ExecuteAfterAttribute[]) Attribute.GetCustomAttributes((MemberInfo) type, typeof (ExecuteAfterAttribute)))
        {
          if (customAttribute.OrderIncrease < 0)
            NetickLogger.LogError((object) $"Script {type.Name} has an [ExecuteAfter] attribute with a negative orderIncrease. Use the [ExecuteBefore] attribute instead. Ignoring this [ExecuteAfter] attribute.");
          else if (!typeof (INetickScript).IsAssignableFrom(customAttribute.TargetType))
          {
            NetickLogger.LogError((object) $"Script {type.Name} has an [ExecuteAfter] attribute with targetScript={customAttribute.TargetType.Name} which is not a MonoBehaviour nor a ScriptableObject. Ignoring this [ExecuteAfter] attribute.");
          }
          else
          {
            ExecutionOrderSystem.ScriptExecutionOrderDependency executionOrderDependency = new ExecutionOrderSystem.ScriptExecutionOrderDependency()
            {
              FirstScript = customAttribute.TargetType,
              SecondScript = type,
              OrderDelta = customAttribute.OrderIncrease
            };
            orderDependencies.Add(executionOrderDependency);
          }
        }
      }
      if (flag3)
      {
        if (flag1)
          NetickLogger.LogError((object) $"Script {type.Name} has both [ExecutionOrder] and [ExecuteBefore] attributes. Ignoring the [ExecuteBefore] attribute.");
        else if (flag2)
        {
          NetickLogger.LogError((object) $"Script {type.Name} has both [ExecuteAfter] and [ExecuteBefore] attributes. Ignoring the [ExecuteBefore] attribute.");
        }
        else
        {
          foreach (ExecuteBeforeAttribute customAttribute in (ExecuteBeforeAttribute[]) Attribute.GetCustomAttributes((MemberInfo) type, typeof (ExecuteBeforeAttribute)))
          {
            if (customAttribute.OrderDecrease < 0)
              NetickLogger.LogError((object) $"Script {type.Name} has an [ExecuteBefore] attribute with a negative orderDecrease. Use the [ExecuteAfter] attribute instead. Ignoring this [ExecuteBefore] attribute.");
            else if (!typeof (INetickScript).IsAssignableFrom(customAttribute.TargetType))
            {
              NetickLogger.LogError((object) $"Script {type.Name} has an [ExecuteBefore] attribute with targetScript={customAttribute.TargetType.Name} which is not a MonoBehaviour nor a ScriptableObject. Ignoring this [ExecuteBefore] attribute.");
            }
            else
            {
              ExecutionOrderSystem.ScriptExecutionOrderDependency executionOrderDependency = new ExecutionOrderSystem.ScriptExecutionOrderDependency()
              {
                FirstScript = customAttribute.TargetType,
                SecondScript = type,
                OrderDelta = -customAttribute.OrderDecrease
              };
              orderDependencies.Add(executionOrderDependency);
            }
          }
        }
      }
    }
    return orderDependencies;
  }

  private static List<ExecutionOrderSystem.ScriptExecutionOrderDefinition> GetExecutionOrderDefinitions(
    List<Type> types)
  {
    List<ExecutionOrderSystem.ScriptExecutionOrderDefinition> orderDefinitions = new List<ExecutionOrderSystem.ScriptExecutionOrderDefinition>();
    foreach (Type type in types)
    {
      if (ExecutionOrderSystem.HasAttribute(typeof (ExecutionOrderAttribute), type))
      {
        ExecutionOrderAttribute customAttribute = (ExecutionOrderAttribute) Attribute.GetCustomAttribute((MemberInfo) type, typeof (ExecutionOrderAttribute));
        ExecutionOrderSystem.ScriptExecutionOrderDefinition executionOrderDefinition = new ExecutionOrderSystem.ScriptExecutionOrderDefinition()
        {
          Script = type,
          Order = customAttribute.Order
        };
        orderDefinitions.Add(executionOrderDefinition);
      }
    }
    return orderDefinitions;
  }

  private static Dictionary<Type, int> GetInitialExecutionOrder(
    List<ExecutionOrderSystem.ScriptExecutionOrderDefinition> definitions,
    List<Type> graphRoots,
    bool useZeroInital = false)
  {
    Dictionary<Type, int> initialExecutionOrder = new Dictionary<Type, int>();
    foreach (ExecutionOrderSystem.ScriptExecutionOrderDefinition definition in definitions)
    {
      Type script = definition.Script;
      int order = definition.Order;
      initialExecutionOrder.Add(script, order);
    }
    foreach (Type graphRoot in graphRoots)
    {
      if (!initialExecutionOrder.ContainsKey(graphRoot))
      {
        int num = 0;
        initialExecutionOrder.Add(graphRoot, num);
      }
    }
    return initialExecutionOrder;
  }

  private static bool HasAttribute(Type attr, Type def)
  {
    return Attribute.IsDefined((MemberInfo) def, attr);
  }

  private static class Graph
  {
    public static Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>> Create(
      List<ExecutionOrderSystem.ScriptExecutionOrderDefinition> definitions,
      List<ExecutionOrderSystem.ScriptExecutionOrderDependency> dependencies)
    {
      Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>> dictionary = new Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>>();
      foreach (ExecutionOrderSystem.ScriptExecutionOrderDependency dependency in dependencies)
      {
        Type firstScript = dependency.FirstScript;
        Type secondScript = dependency.SecondScript;
        List<ExecutionOrderSystem.Graph.Edge> edgeList;
        if (!dictionary.TryGetValue(firstScript, out edgeList))
        {
          edgeList = new List<ExecutionOrderSystem.Graph.Edge>();
          dictionary.Add(firstScript, edgeList);
        }
        edgeList.Add(new ExecutionOrderSystem.Graph.Edge()
        {
          node = secondScript,
          weight = dependency.OrderDelta
        });
        if (!dictionary.ContainsKey(secondScript))
          dictionary.Add(secondScript, new List<ExecutionOrderSystem.Graph.Edge>());
      }
      foreach (ExecutionOrderSystem.ScriptExecutionOrderDefinition definition in definitions)
      {
        Type script = definition.Script;
        if (!dictionary.ContainsKey(script))
          dictionary.Add(script, new List<ExecutionOrderSystem.Graph.Edge>());
      }
      return dictionary;
    }

    private static bool IsCyclicRecursion(
      Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>> graph,
      Type node,
      Dictionary<Type, bool> visited,
      Dictionary<Type, bool> inPath)
    {
      if (!visited[node])
      {
        visited[node] = true;
        inPath[node] = true;
        foreach (ExecutionOrderSystem.Graph.Edge edge in graph[node])
        {
          Type node1 = edge.node;
          if (ExecutionOrderSystem.Graph.IsCyclicRecursion(graph, node1, visited, inPath))
          {
            inPath[node] = false;
            return true;
          }
        }
        inPath[node] = false;
        return false;
      }
      return inPath[node];
    }

    public static bool IsCyclic(
      Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>> graph)
    {
      Dictionary<Type, bool> visited = new Dictionary<Type, bool>();
      Dictionary<Type, bool> inPath = new Dictionary<Type, bool>();
      foreach (Type key in graph.Keys)
      {
        visited.Add(key, false);
        inPath.Add(key, false);
      }
      foreach (Type key in graph.Keys)
      {
        if (ExecutionOrderSystem.Graph.IsCyclicRecursion(graph, key, visited, inPath))
          return true;
      }
      return false;
    }

    public static List<Type> GetRoots(
      Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>> graph)
    {
      Dictionary<Type, int> dictionary = new Dictionary<Type, int>();
      foreach (Type key in graph.Keys)
        dictionary.Add(key, 0);
      foreach (KeyValuePair<Type, List<ExecutionOrderSystem.Graph.Edge>> keyValuePair in graph)
      {
        Type key = keyValuePair.Key;
        foreach (ExecutionOrderSystem.Graph.Edge edge in keyValuePair.Value)
        {
          Type node = edge.node;
          dictionary[node]++;
        }
      }
      List<Type> roots = new List<Type>();
      foreach (KeyValuePair<Type, int> keyValuePair in dictionary)
      {
        Type key = keyValuePair.Key;
        if (keyValuePair.Value == 0)
          roots.Add(key);
      }
      return roots;
    }

    public static void PropagateValues(
      Dictionary<Type, List<ExecutionOrderSystem.Graph.Edge>> graph,
      Dictionary<Type, int> values)
    {
      Queue<Type> typeQueue = new Queue<Type>();
      foreach (Type key in values.Keys)
        typeQueue.Enqueue(key);
      while (typeQueue.Count > 0)
      {
        Type key = typeQueue.Dequeue();
        int num1 = values[key];
        foreach (ExecutionOrderSystem.Graph.Edge edge in graph[key])
        {
          Type node = edge.node;
          int num2 = num1 + edge.weight;
          int num3;
          if ((values.TryGetValue(node, out num3) ? 1 : 0) == 0 | (edge.weight > 0 ? num2 > num3 : num2 < num3))
          {
            values[node] = num2;
            typeQueue.Enqueue(node);
          }
        }
      }
    }

    public struct Edge
    {
      public Type node;
      public int weight;
    }
  }

  private struct ScriptExecutionOrderDefinition
  {
    public Type Script { get; set; }

    public int Order { get; set; }
  }

  private struct ScriptExecutionOrderDependency
  {
    public Type FirstScript { get; set; }

    public Type SecondScript { get; set; }

    public int OrderDelta { get; set; }
  }
}
